package com.beiding.hotcode.starter.T;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Set;

public class F {

    public static final MyFactory g;

    static {
        F f = F.create("C:\\Users\\dinglei\\Desktop\\内部类支持\\target\\hotcode-2.0.2-RELEASE-jar-with-dependencies.jar");

        HotCodeConfig config = f.new HotCodeConfig();

        config.encoding("utf8");
        config.srcRoot("C:\\Users\\dinglei\\Desktop\\内部类支持\\src\\main\\java");
        g = f.new MyFactory(config);
    }

    //必须给定
    public static F create(String hotcodeClasspath) {
        File hotcodeClasspathFile = new File(hotcodeClasspath);
        if (!hotcodeClasspathFile.exists()) {
            return null;
        }
        try {
            Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            addURL.setAccessible(true);
            addURL.invoke(Thread.currentThread().getContextClassLoader(), hotcodeClasspathFile.toURI().toURL());
            return new F();
        } catch (Exception e) {

            e.printStackTrace();
            return null;
        }
    }

    private class Base {

        //真实对象
        Object obj;
    }


    private Object nativeNewInstance(String type, Object... params) {

        try {

            Object[] rp = new Object[params.length];

            for (int i = 0; i < params.length; i++) {
                Object p = params[i];

                if (p instanceof Base) {
                    p = ((Base) p).obj;
                }
                rp[i] = p;

            }

            Class theClass = Class.forName("com.beiding.hotcode.v2." + type);

            Constructor[] constructors = theClass.getDeclaredConstructors();

            Constructor constructor = null;

            out:
            for (Constructor c : constructors) {
                Class[] parameterTypes = c.getParameterTypes();
                if (parameterTypes.length != params.length) {
                    continue;
                }
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (params[i] == null) {
                        continue;
                    }

                    if (!parameterTypes[i].isInstance(rp[i])) {
                        continue out;
                    }

                }
                if (constructor != null) {
                    StringBuilder builder = new StringBuilder();

                    if (params.length > 0) {
                        builder.append(params[0] == null ? "null" : params[0].getClass().getName());
                        for (int i = 1; i < params.length; i++) {
                            builder.append(",").append(params[i] == null ? "null" : params[i].getClass().getName());
                        }
                    }

                    throw new RuntimeException("无法确定构造函数:" + theClass.getName() + "(" + builder.toString() + ")");
                }
                constructor = c;
            }
            if (constructor == null) {

                StringBuilder builder = new StringBuilder();

                if (params.length > 0) {
                    builder.append(params[0] == null ? "null" : params[0].getClass().getName());
                    for (int i = 1; i < params.length; i++) {
                        builder.append(",").append(params[i] == null ? "null" : params[i].getClass().getName());
                    }
                }

                throw new RuntimeException("无法确定构造函数:" + theClass.getName() + "(" + builder.toString() + ")");
            }

            constructor.setAccessible(true);

            return constructor.newInstance(rp);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Object nativeInvoke(Object obj, String method, Class[] types, Object[] params) {

        Class[] realTypes = new Class[types.length];

        Object[] realParams = new Object[params.length];

        for (int i = 0; i < realTypes.length; i++) {
            if (Base.class == types[i].getSuperclass()) {
                try {
                    Class theClass = Class.forName("com.beiding.hotcode.v2." + types[i].getSimpleName());
                    realTypes[i] = theClass;
                    realParams[i] = ((Base) params[i]).obj;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

            } else {
                realParams[i] = params[i];
                realTypes[i] = types[i];
            }
        }

        try {
            Class c = obj.getClass();
            Method m = c.getMethod(method, realTypes);
            m.setAccessible(true);
            return m.invoke(obj, realParams);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


    //-------------------


    public class ClassGroup extends Base {
        ClassGroup(MyFactory p0) {
            this.obj = nativeNewInstance("ClassGroup", p0);
        }

        public void addClass(MyClass p0) {
            nativeInvoke(obj, "addClass", new Class[]{MyClass.class}, new Object[]{p0});
        }

        public void removeClass(MyClass p0) {
            nativeInvoke(obj, "removeClass", new Class[]{MyClass.class}, new Object[]{p0});
        }
    }

    public class ClasspathContext extends Base {
        public ClasspathContext() {
            this.obj = nativeNewInstance("ClasspathContext");
        }

        public void add(MyClass p0) {
            nativeInvoke(obj, "add", new Class[]{MyClass.class}, new Object[]{p0});
        }

        public void add(MyPackage p0) {
            nativeInvoke(obj, "add", new Class[]{MyPackage.class}, new Object[]{p0});
        }

        public void remove(MyClass p0) {
            nativeInvoke(obj, "remove", new Class[]{MyClass.class}, new Object[]{p0});
        }

        public void remove(MyPackage p0) {
            nativeInvoke(obj, "remove", new Class[]{MyPackage.class}, new Object[]{p0});
        }

        public void removeClassPath(File p0) {
            nativeInvoke(obj, "removeClassPath", new Class[]{File.class}, new Object[]{p0});
        }

        public void addClassPath(File p0) {
            nativeInvoke(obj, "addClassPath", new Class[]{File.class}, new Object[]{p0});
        }
    }

    public class HotCodeConfig extends Base {
        public HotCodeConfig() {
            this.obj = nativeNewInstance("HotCodeConfig");
        }

        public String getEncoding() {
            return (String) nativeInvoke(obj, "getEncoding", new Class[]{}, new Object[]{});
        }

        public HotCodeConfig encoding(String p0) {
            Object r = nativeInvoke(obj, "encoding", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            HotCodeConfig t = new HotCodeConfig();
            t.obj = r;
            return t;
        }

        public Set getOtherClasspath() {
            return (Set) nativeInvoke(obj, "getOtherClasspath", new Class[]{}, new Object[]{});
        }

        public HotCodeConfig otherClasspath(Set p0) {
            Object r = nativeInvoke(obj, "otherClasspath", new Class[]{Set.class}, new Object[]{p0});
            if (r == null) return null;
            HotCodeConfig t = new HotCodeConfig();
            t.obj = r;
            return t;
        }

        public HotCodeConfig srcRoot(String p0) {
            Object r = nativeInvoke(obj, "srcRoot", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            HotCodeConfig t = new HotCodeConfig();
            t.obj = r;
            return t;
        }

        public String getSrcRoot() {
            return (String) nativeInvoke(obj, "getSrcRoot", new Class[]{}, new Object[]{});
        }
    }

    //TODO 生命后期回调
    public abstract class InstanceLifecycle extends Base {

        //TODO
        public InstanceLifecycle() {

            try {
                Class theClass = Class.forName("com.beiding.hotcode.v2.InstanceLifecycleReflect");
                Constructor constructor = theClass.getConstructor(Object.class);
                this.obj = constructor.newInstance(this);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }

        public void afterCreate(Object obj) {
            MyInstance myInstance = new MyInstance();
            myInstance.obj = obj;
            onAfterCreate(myInstance);
        }

        public void beforeAbandon(Object obj) {
            MyInstance myInstance = new MyInstance();
            myInstance.obj = obj;
            onBeforeAbandon(myInstance);
        }

        public abstract void onAfterCreate(MyInstance myInstance);

        public abstract void onBeforeAbandon(MyInstance myInstance);

    }

    public class MyClass<TYPE> extends Base {

        public TYPE newObject(Object... p0) {
            return (TYPE) nativeInvoke(obj, "newObject", new Class[]{Object[].class}, new Object[]{p0});
        }

        public Object invoke(String p0, Object... p1) {
            return (Object) nativeInvoke(obj, "invoke", new Class[]{String.class, Object[].class}, new Object[]{p0, p1});
        }

        public Object get(String p0, Class p1) {
            return (Object) nativeInvoke(obj, "get", new Class[]{String.class, Class.class}, new Object[]{p0, p1});
        }

        public Object get(String p0) {
            return (Object) nativeInvoke(obj, "get", new Class[]{String.class}, new Object[]{p0});
        }

        public MyInstance newInstance(Object... p0) {
            Object r = nativeInvoke(obj, "newInstance", new Class[]{Object[].class}, new Object[]{p0});
            if (r == null) return null;
            MyInstance t = new MyInstance();
            t.obj = r;
            return t;
        }

        public Class getType() {
            return (Class) nativeInvoke(obj, "getType", new Class[]{}, new Object[]{});
        }

        public void set(String p0, Object p1) {
            nativeInvoke(obj, "set", new Class[]{String.class, Object.class}, new Object[]{p0, p1});
        }

        public MyMethod method(String p0) {
            Object r = nativeInvoke(obj, "method", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            MyMethod t = new MyMethod();
            t.obj = r;
            return t;
        }

        public MyMethod method(String p0, Class p1) {
            Object r = nativeInvoke(obj, "method", new Class[]{String.class, Class.class}, new Object[]{p0, p1});
            if (r == null) return null;
            MyMethod t = new MyMethod();
            t.obj = r;
            return t;
        }

        public Object invokeCatch(String p0, Object... p1) {
            return (Object) nativeInvoke(obj, "invokeCatch", new Class[]{String.class, Object[].class}, new Object[]{p0, p1});
        }
    }

    public class MyFactory extends Base {
        public MyFactory(HotCodeConfig p0) {
            this.obj = nativeNewInstance("MyFactory", p0);
        }

        public <TYPE> TYPE wrap(TYPE p0) {
            return (TYPE) nativeInvoke(obj, "wrap", new Class[]{Object.class}, new Object[]{p0});
        }

        public MyPackage getPackage(String p0) {
            Object r = nativeInvoke(obj, "getPackage", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            MyPackage t = new MyPackage();
            t.obj = r;
            return t;
        }

        public PackageGroup createPackageGroup() {
            Object r = nativeInvoke(obj, "createPackageGroup", new Class[]{}, new Object[]{});
            if (r == null) return null;
            PackageGroup t = new PackageGroup();
            t.obj = r;
            return t;
        }

        public ClassGroup createClassGroup() {
            Object r = nativeInvoke(obj, "createClassGroup", new Class[]{}, new Object[]{});
            if (r == null) return null;
            ClassGroup t = new ClassGroup(this);
            t.obj = r;
            return t;
        }

        public void openHot() {
            nativeInvoke(obj, "openHot", new Class[]{}, new Object[]{});
        }

        public MyPackage getPackage(Class p0) {
            Object r = nativeInvoke(obj, "getPackage", new Class[]{Class.class}, new Object[]{p0});
            if (r == null) return null;
            MyPackage t = new MyPackage();
            t.obj = r;
            return t;
        }

        public <TYPE> MyClass<TYPE> getMyClass(Class<TYPE> p0) {
            Object r = nativeInvoke(obj, "getMyClass", new Class[]{Class.class}, new Object[]{p0});
            if (r == null) return null;
            MyClass t = new MyClass();
            t.obj = r;
            return t;
        }

        public MyClass getMyClass(String p0) {
            Object r = nativeInvoke(obj, "getMyClass", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            MyClass t = new MyClass();
            t.obj = r;
            return t;
        }

        public boolean isHot() {
            return (boolean) nativeInvoke(obj, "isHot", new Class[]{}, new Object[]{});
        }

        public void closeHot() {
            nativeInvoke(obj, "closeHot", new Class[]{}, new Object[]{});
        }
    }

    public class MyInstance extends Base {

        public Object invoke(String p0, Object... p1) {
            return (Object) nativeInvoke(obj, "invoke", new Class[]{String.class, Object[].class}, new Object[]{p0, p1});
        }

        public Object get(String p0, Class p1) {
            return (Object) nativeInvoke(obj, "get", new Class[]{String.class, Class.class}, new Object[]{p0, p1});
        }

        public Object get(String p0) {
            return (Object) nativeInvoke(obj, "get", new Class[]{String.class}, new Object[]{p0});
        }

        public Object getInstance() {
            return (Object) nativeInvoke(obj, "getInstance", new Class[]{}, new Object[]{});
        }

        public Class getType() {
            return (Class) nativeInvoke(obj, "getType", new Class[]{}, new Object[]{});
        }

        public void set(String p0, Object p1) {
            nativeInvoke(obj, "set", new Class[]{String.class, Object.class}, new Object[]{p0, p1});
        }

        public MyMethod method(String p0, Class p1) {
            Object r = nativeInvoke(obj, "method", new Class[]{String.class, Class.class}, new Object[]{p0, p1});
            if (r == null) return null;
            MyMethod t = new MyMethod();
            t.obj = r;
            return t;
        }

        public MyMethod method(String p0) {
            Object r = nativeInvoke(obj, "method", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            MyMethod t = new MyMethod();
            t.obj = r;
            return t;
        }

        public MyClass getMyClass() {
            Object r = nativeInvoke(obj, "getMyClass", new Class[]{}, new Object[]{});
            if (r == null) return null;
            MyClass t = new MyClass();
            t.obj = r;
            return t;
        }

        public Object invokeCatch(String p0, Object... p1) {
            return (Object) nativeInvoke(obj, "invokeCatch", new Class[]{String.class, Object[].class}, new Object[]{p0, p1});
        }

        public void setInstanceLifecycle(InstanceLifecycle p0) {
            nativeInvoke(obj, "setInstanceLifecycle", new Class[]{InstanceLifecycle.class}, new Object[]{p0});
        }

        public <T> T forceGet(String p0, Class<T> p1) {
            return (T) nativeInvoke(obj, "forceGet", new Class[]{String.class, Class.class}, new Object[]{p0, p1});
        }

        public Object forceGet(String p0) {
            return (Object) nativeInvoke(obj, "forceGet", new Class[]{String.class}, new Object[]{p0});
        }

        public void forceSet(String p0, Object p1) {
            nativeInvoke(obj, "forceSet", new Class[]{String.class, Object.class}, new Object[]{p0, p1});
        }
    }

    public class MyMethod extends Base {
        public Object invoke(Object... p0) {
            return (Object) nativeInvoke(obj, "invoke", new Class[]{Object[].class}, new Object[]{p0});
        }

        public Object invokeCatch(Object... p0) {
            return (Object) nativeInvoke(obj, "invokeCatch", new Class[]{Object[].class}, new Object[]{p0});
        }
    }

    public class MyPackage extends Base {
        public MyClass getMyClassBySimpleName(String p0) {
            Object r = nativeInvoke(obj, "getMyClassBySimpleName", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            MyClass t = new MyClass();
            t.obj = r;
            return t;
        }

        public <TYPE> MyClass<TYPE> getMyClass(Class<TYPE> p0) {
            Object r = nativeInvoke(obj, "getMyClass", new Class[]{Class.class}, new Object[]{p0});
            if (r == null) return null;
            MyClass t = new MyClass();
            t.obj = r;
            return t;
        }

        public MyClass getMyClass(String p0) {
            Object r = nativeInvoke(obj, "getMyClass", new Class[]{String.class}, new Object[]{p0});
            if (r == null) return null;
            MyClass t = new MyClass();
            t.obj = r;
            return t;
        }
    }

    public class PackageGroup extends Base {
        PackageGroup() {
            this.obj = nativeNewInstance("PackageGroup");
        }

        public void add(MyPackage p0) {
            nativeInvoke(obj, "add", new Class[]{MyPackage.class}, new Object[]{p0});
        }

        public void remove(MyPackage p0) {
            nativeInvoke(obj, "remove", new Class[]{MyPackage.class}, new Object[]{p0});
        }
    }


}
